home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / tjgold.zip / INSTALL.004 / FGUSER13.TXT < prev    next >
Text File  |  1995-05-29  |  32KB  |  805 lines

  1.  
  2.                         Single and Double Linked Lists
  3.  
  4.                                    "Time will run back and fetch the age
  5.                                     of gold and speckled vanity will
  6.                                     sicken soon and die."
  7.                                                                  Milton
  8.  
  9. Arrays Versus Lists
  10.  
  11.           Linked lists are invaluable and should form a strategic part
  12.      of every programmer's arsenal. If you are not familiar with linked
  13.      lists, you have probably been using arrays to meet your list
  14.      management needs. But arrays have serious shortcomings.
  15.  
  16.           One-dimensional arrays provide a very quick and convenient way
  17.      of managing lists. An array is a data structure designed to store
  18.      data about a fixed number of identical elements. For example, the
  19.      following variable could be used to store the addresses of my two
  20.      sisters and three brothers:
  21.  
  22.           Addresses: array [1..5] of string
  23.  
  24.           Data in the array is accessed by specifying the element
  25.      number. For example, the following statement would update my second
  26.      sister's address:
  27.  
  28.           Addresses[2] := '38 The Ridgeway, Friern Barnet London'
  29.  
  30.           Arrays are very easy to create, but suffer from two major
  31.      weaknesses:
  32.  
  33.           The size of the array is fixed at compile time. In other
  34.      words, in your source code you must explicitly state the size (or
  35.      bounds) of the array. In our simple example, this was not a problem
  36.      because I only have 5 siblings. Imagine, however, that you need to
  37.      write a generic name and address program. You don't know how many
  38.      names and addresses will be required, but you must specify the
  39.      number in your program. The typical solution is to choose a large
  40.      number of elements (e.g. 200) and hope that the user doesn't need
  41.      more. Not only does this impose a limit on the program's capacity,
  42.      it also wastes memory if the maximum number of elements is not
  43.      reached. Borland Pascal automatically allocates the memory for the
  44.      entire array regardless of how many elements are actually used.
  45.      Which leads to the second shortcoming.
  46.  
  47.           An individual data item must be 64k or less, and the entire
  48.      array is considered to be an individual item. In our example, each
  49.      element of the array is 256 bytes, so 256 of these elements would
  50.      consume the entire 64k. In many cases arrays simply do not have the
  51.      capacity to meet today's data needs.
  52.  
  53.           Linked lists do not suffer from these shortcomings. With a
  54.      linked list, each element is stored independently. The first
  55.      element includes a pointer indicating where the second element in
  56.      the list is stored. The second element of the list includes a
  57.      pointer to the third element in the list, and so on. When you reach
  58.      an element in the list that points to NIL, you know you have
  59.      reached the end of the list. You do not need to fix the maximum
  60.      number of elements in the list at compile time. Furthermore, the
  61.      data is stored on the heap (not in the data segment) and so is not
  62.      limited to 64k. The list can use all available memory.
  63.  
  64.           The good news is that you do not need to understand all the
  65.      details of how linked lists work. Gold takes care of all the
  66.      internal list management mechanics.
  67.  
  68. Single and Double Lists
  69.  
  70.           Gold offers singly and doubly linked lists. You need to
  71.      understand a little list theory in order to decide which list type
  72.      is appropriate for your application.
  73.  
  74.           A list is actually a collection of nodes. A node contains two
  75.      primary elements: data, and one or two node pointers. The data is
  76.      the information stored in the list, e.g. the names and addresses in
  77.      the address book. The node pointer points to the adjacent node in
  78.      the list, and provides a link between two nodes.
  79.  
  80.           In a single linked list, there is a single pointer at each
  81.      node, which points to the next item in the list. If you are
  82.      currently accessing the 100th node in the list and want to get the
  83.      data from the 99th node, Gold would have to go all the way to the
  84.      beginning of the list, find the pointer to the second node, go to
  85.      the second node, find the pointer to the third node, go to the
  86.      third node, and so on all the way to the 99th element.
  87.  
  88.           In a double linked list, there are two pointers at each node.
  89.      One pointer points to the previous item in the list, and the other
  90.      to the next item in the list. These are referred to as the next and
  91.      prev pointers. In the previous example, Gold would just have to use
  92.      the previous pointer to move to the 99th node in one step.
  93.  
  94.           Given this brief explanation of the two linked lists types,
  95.      you might be wondering why anyone bothers with single linked lists
  96.      since double linked lists are more powerful. The answer is two
  97.      part: complexity and memory. Double linked lists are more
  98.      complicated to manage than single linked lists, especially when
  99.      inserting and deleting nodes. But, hey, you don't care because Gold
  100.      hides all the complexity from you! Every node in a double linked
  101.      list consumes 4 more bytes of memory (for the previous pointer)
  102.      than the equivalent node in a single linked list. Although 4 bytes
  103.      doesn't sound much, it can add up when you have several thousand
  104.      nodes.
  105.  
  106.           If your lists will be short (say, 150 items or less) and you
  107.      won't be going backwards along the list too often, use a single
  108.      linked list. Otherwise, use a double linked list.
  109.  
  110. The Three Linked List Types
  111.  
  112.           So far we have discussed two primary linked list types: single
  113.      and double. Gold actually provides three different types of linked
  114.      lists: two single linked lists and one double linked list. To
  115.      create a linked list you must declare a variable of one of the
  116.      following types:
  117.  
  118.           List Type  Description
  119.  
  120.           StringLL   This is a single linked list designed sepcifically
  121.                      for managing short lists of strings. These lists
  122.                      have a low code overhead and can only be used for
  123.                      storing strings. Behind the scenes, Gold uses this
  124.                      list type for managing menu items, and several
  125.                      other sets of small string lists such as the drive
  126.                      list in the PromptDir dialog.
  127.  
  128.           SingleLL   A single linked list can be used for storing
  129.                      strings, records or binary data, and uses a single
  130.                      next pointer for node traversal.
  131.  
  132.           DoubleLL   A double linked list can be used for storing
  133.                      strings, records or binary data, and uses a single
  134.                      next pointer for node traversal. List sorting is
  135.                      supported.
  136.  
  137.           The techniques for managing each of these list types are
  138.      discussed in the following sections. In brief, all you do is
  139.      declare a variable of the appropriate type (e.g. MyList: SingleLL),
  140.      call an init procedure to initialize the variable, then call a
  141.      series of add functions to add nodes to the list, and when you are
  142.      finished with the list call the appropriate destroy procedure.
  143.  
  144. Using StringLLs
  145.  
  146.           The StringLL is really the baby of the Gold's linked list
  147.      family. It can only store strings and shouldn't be used to store
  148.      more than forty (or so) elements, otherwise performance will
  149.      suffer. Having said that, there are literally hundreds of
  150.      situations where all you need is a list to manage  several dozen
  151.      strings. It might be simple, but it's very useful.
  152.  
  153. Initializing the StringLL
  154.  
  155.           Before you can add items to (or populate) a StringLL it must,
  156.      must, must be initialized using the procedure StrLLInit. For
  157.      example:
  158.  
  159.           StrLLInit(MyList);
  160.  
  161.           All the names of procedures used to manipulate a StringLL list
  162.      are prefixed with the characters "STRLL".
  163.  
  164. Building the List
  165.  
  166.           Having initialized the list, you can add strings to the list
  167.      using the following procedure:
  168.  
  169.           StrLLAdd(var SL:StringLL; Str:String): integer;
  170.  
  171.           Adds a new string node at the end of the passed StringLL. The
  172.      function returns an integer indicating the success of the
  173.      operation: a 0 indicates the item was successfully added and a 1
  174.      indicates failure (probably due to a lack of memory).
  175.  
  176.           There is no way to change data in StringLL, nor can you delete
  177.      a node or insert a node. As McDonalds might say: "What you add is
  178.      what you get"! If you need more list manipulation tools, switch to
  179.      a SingleLL or DoubleLL.
  180.  
  181. Getting String Data out of the List
  182.  
  183.           The following function is used to get a string from a linked
  184.      list:
  185.  
  186.           StrLLGetStr(var SL:StringLL;Num:integer): string;
  187.  
  188.           The function is passed a StringLL and a (one-based) node
  189.      number and returns the string stored at that node.
  190.  
  191. Destroying the StringLL
  192.  
  193.           After you have finished with the list, always destroy the
  194.      list, and in so doing free up the memory used to create the list
  195.      with the following procedure:
  196.  
  197.           StrLLDone(var SL:StringLL);
  198.  
  199.      Frees the memory used by all the nodes in a StringLL.
  200.  
  201. A StringLL Example
  202.  
  203.           Listed below is an extract of the DEMLNK1.PAS demo file which
  204.      creates a StringLL and then displays the list contents using the
  205.      PromptOKStrLL procedure.
  206.  
  207.           var
  208.              I: integer;
  209.              TextList: StringLL;
  210.  
  211.           begin
  212.              I := 0;
  213.              StrLLInit(TextList);
  214.              inc(I,StrLLAdd(TextList,'Thank ...'));
  215.              inc(I,StrLLAdd(TextList,''));
  216.              inc(I,StrLLAdd(TextList,'This ...'));
  217.              inc(I,StrLLAdd(TextList,'of ...'));
  218.              inc(I,StrLLAdd(TextList,'product to others.'));
  219.              inc(I,StrLLAdd(TextList,''));
  220.              inc(I,StrLLAdd(TextList,'A ...'));
  221.              inc(I,StrLLAdd(TextList,'in ...'));
  222.              inc(I,StrLLAdd(TextList,'of the ....'));
  223.              if I = 0 then {all's well}
  224.                 PromptOKStrLL(' Welcome ',TextList)
  225.              else
  226.                 PromptOK(' Ouch! ',' Not enough memory');
  227.              StrLLDestroy(TextList);
  228.           end.
  229.  
  230.  
  231.           Notice the clever trick to avoid testing the return codes from
  232.      the StrLLAdd function. "I" is incremented by the return code of
  233.      each StrLLAdd call. If "I" is zero after all the calls, then every
  234.      add operation must have been successful. The code make look a
  235.      little strange, but it is a useful technique. Understanding Node
  236.      Pointers
  237.  
  238.           To get the most from Gold's SingleLL and DoubleLL linked lists
  239.      you need to have a modest understanding of node pointers.
  240.  
  241.           As you know, a linked list is a collection of nodes, with each
  242.      node pointing to the next node in the list. To access the data
  243.      stored at a node, you need to specify which node you want to
  244.      access. This is done using a node pointer.
  245.  
  246.           For example, to change a string stored in a SingleLL you would
  247.      use the function SLLChangeStr, and pass a pointer to the node along
  248.      with the replacement string. If you wanted to change the string
  249.      stored at the 20th node, for example, you will need to obtain a
  250.      pointer to the node. Fortunately Gold includes the function
  251.      SLLNodePtr expressly for this purpose. You pass a node number and
  252.      the function returns a pointer to the node.
  253.  
  254.           The following code fragment shows how this might be
  255.      implemented in a program:
  256.  
  257.           var
  258.              SNP: SingleNodePtr;
  259.              Result: integer;
  260.           begin
  261.              ...
  262.              SNP := SLLNodePtr(20);
  263.              Result := SLLChangeStr(SNP,'New string');
  264.              ...
  265.           end.
  266.  
  267.           Alternatively, you could avoid the need for a variable and
  268.      combine the two calls into one expression as follows:
  269.  
  270.           var
  271.              Result: integer;
  272.           begin
  273.              ...
  274.              Result := SLLChangeStr(SLLNodePtr(20),'New string');
  275.              ...
  276.           end.
  277.  
  278.           Anywhere that a SingleNodePtr is required as a parameter, you
  279.      can substitute the function SLLNodePtr.
  280.  
  281.           The examples (above) refer to single linked lists, but the
  282.      same goes for double linked lists -- use the function DLLNodePtr to
  283.      a return node pointer of type DoubleNodePtr.
  284.  
  285. Using SingleLLs
  286.  
  287.           The SingleLL type is used to create a single linked list. Gold
  288.      includes a generic set of routines for managing any data in a
  289.      SingleLL. However, because many applications used linked lists to
  290.      store strings, there is also a set of routines designed especially
  291.      for single linked lists of strings.
  292.  
  293. Initializing a SingleLL Variable
  294.  
  295.           A SingleLL variable must be initialized before data can be
  296.      added to the list. There are two procedures for initializing
  297.      SingleLLs: one for generic lists and one for string lists, as
  298.      follows:
  299.  
  300.           InitSLL(var TheList:SingleLL);
  301.  
  302.           Initializes a single linked list and sets the list for storing
  303.      any data.
  304.  
  305.           InitSLLStr(var TheList:SingleLL);
  306.  
  307.           Initializes a single linked list and sets the list for storing
  308.      strings.
  309.  
  310.           The type SingleLL is declared in GOLDLINK as follows:
  311.  
  312.           SingleLL = record
  313.              StartNodePtr: SingleNodePtr;
  314.              EndNodePtr: SingleNodePtr;
  315.              TotalNodes: longint;
  316.              StrVars: boolean;
  317.              Dirty: boolean;
  318.           end; {SingleLL}
  319.  
  320. Giving a SingleLL Focus
  321.  
  322.           Gold allows an application to have many linked lists active at
  323.      the same time, i.e. two or more lists in the same scope. Gold needs
  324.      to know which list to manipulate. Before calling the functions to
  325.      add, modify and delete data in a list, you must instruct Gold to
  326.      give a single linked list focus using the following procedure:
  327.  
  328.           SLLSetActiveList(var S:SingleLL);
  329.  
  330.           Instructs Gold to direct all single linked list management
  331.      functions to the specified list.
  332.  
  333.           If you are managing multiple lists, you can use the procedure
  334.      SLLActivatePrevList to change the focus back to the list which had
  335.      focus prior to the last SLLSetActiveList call.
  336.  
  337. Adding and Changing List Data
  338.  
  339.           Once a list has been given focus, you can use the following
  340.      procedures and functions to add and modify the list data:
  341.  
  342.           SLLAdd(var TheData; Size:longint): integer;
  343.  
  344.           Adds data of any type to a new node at the end of a single
  345.      linked list. The function is passed a variable, followed by a
  346.      longint indicating the size of the variable. The function returns a
  347.      0 if successful.
  348.  
  349.           SLLInsertBefore(Node:SingleNodePtr;var TheData;Size:longint):
  350.           integer;
  351.  
  352.           Inserts a new node in front of the node specified by the node
  353.      pointer. The function returns a 0 if successful.
  354.  
  355.           SLLChange(Node:SingleNodePtr;var TheData;Size:longint):
  356.           integer;
  357.  
  358.           Modifies the data stored at the specified node. The procedure
  359.      is passed a pointer to the node where the new data will be stored,
  360.      the data variable and the data size. The function returns a 0 if
  361.      successful.
  362.  
  363.           SLLDelNode(Node:SingleNodePtr);
  364.  
  365.           Removes the specified node from the linked list.
  366.  
  367.           SLLGetNodeData(Node:SingleNodePtr;Var TheData);
  368.  
  369.           Retrieves the data at the specified node, and copies the
  370.      contents into the specified variable.
  371.  
  372.           SLLGetNodeDataSize(Node:SingleNodePtr):longint;
  373.  
  374.           Returns the number of bytes of data stored at the specified
  375.      node.
  376.  
  377. Storing Records in a List
  378.  
  379.           Many of the SLL functions accept an untyped variable as one of
  380.      the parameters. This allows the list to manage data of any type,
  381.      e.g. integers, reals, records, enumerated types, etc. But
  382.      ninety-nine percent of applications store either records or strings
  383.      in linked lists.
  384.  
  385.           If you want to save a record in a node, just create a variable
  386.      of the record's type, and then use the SLL add function to copy the
  387.      record to a new node at the end of the list. For example, the
  388.      following code stores a name and address at a node:
  389.  
  390.           type
  391.              Location: record
  392.                 Name: string[20];
  393.                 Address: string[50];
  394.                 Zip: longint;
  395.              end; {Location}
  396.  
  397.           var
  398.             WorkAddress: Location
  399.              AddressList: SingleLL;
  400.  
  401.           begin
  402.              ...
  403.              InitSLL(AddressList);
  404.              SLLSetActiveList(AddressList);
  405.              with WorkAddress do
  406.              begin
  407.                 Name := 'Beavis';
  408.                 Address := '1 Bonehead Park, Dallas TX';
  409.                 Zip := 77665;
  410.              end;
  411.              SLLAdd(WorkAddress,sizeof(Address));
  412.              ...
  413.           end.
  414.  
  415.           That's all there is to it. In a real application you might add
  416.      hundreds of addresses to the list. Retrieving a record from the
  417.      list is just as easy. The following statement will retrieve the
  418.      twelfth address from the list:
  419.  
  420.           SLLGetNodeData(SLLNodePtr(12),WorkAddress);
  421.  
  422. Storing Strings in a List
  423.  
  424.           If you have initialized the list using InitSLLStr (to store
  425.      strings) then you should use the following functions to add, modify
  426.      and access strings in the list:
  427.  
  428.           SLLAddStr(Str:string):integer;
  429.  
  430.           Adds a new node to the end of a single linked list and stores
  431.      the string at the node. The function returns a 0 if successful.
  432.  
  433.           SLLInsStrBefore(Node:SingleNodePtr;Str:string): integer;
  434.  
  435.           Inserts a new node, prior to the node specified by the node
  436.      pointer, and stores a string at the node. The function returns a 0
  437.      if successful.
  438.  
  439.           SLLChangeStr(Node:SingleNodePtr;Str:string): integer;
  440.  
  441.           Modifies the string stored at the specified node. The
  442.      procedure is passed a pointer to the node where the new string will
  443.      be stored, along with the string. The function returns a 0 if
  444.      successful.
  445.  
  446.           SLLGetStr(Num:longint):string;
  447.  
  448.           Returns the string at the specified node number. Note (for
  449.      convenience) that this function accepts a node number and not a
  450.      node pointer.
  451.  
  452. Populating a List from a File
  453.  
  454.           Gold makes it easy to save and restore lists from a text file
  455.      using the following two procedures:
  456.  
  457.           SLLLoadFromFile(Filename:string):integer;
  458.  
  459.           Populates the active linked list from a text file. Each line
  460.      of the text file is a node in the linked list. Any existing data in
  461.      the active linked list is destroyed. The active linked list must be
  462.      initialized as a string linked list. The function returns a zero if
  463.      the read was successful.
  464.  
  465.           SLLSaveToFile(Filename:string):integer;
  466.  
  467.           Saves the contents of the active single linked list to a text
  468.      file. Each node of the list is saved as a line of text in the file.
  469.      The function returns a zero if the save was successful. An existing
  470.      file of the same name will be overwritten.
  471.  
  472. Settings and Getting Bits
  473.  
  474.           Every node in a SingleLL (and a DoubleLL for that matter) has
  475.      a special byte used to store up to eight bit flags. These bit flags
  476.      are numbered from 0 through 7 and bits 0 and 1 are reserved for
  477.      Gold's use -- for item tagging and color separation in list windows
  478.      (discussed in the next chapter). The remaining 6 bits, numbered 2
  479.      through 7, are available for use. The following two routines are
  480.      used to manage these bits.
  481.  
  482.           SLLSetBit(Node:SingleNodePtr; BitPos:byte; On:boolean);
  483.  
  484.           Sets the bit at the specified node to TRUE or FALSE.
  485.  
  486.           SLLGetBit(Node:SingleNodePtr; BitPos:byte): boolean;
  487.  
  488.           Returns TRUE if the specified bit at the node is on, or FALSE
  489.      if it is off.
  490.  
  491. Destroying the List
  492.  
  493.           The nodes in a linked list are constructed on the heap and can
  494.      consume significant amounts of memory. When the list is no longer
  495.      required, always destroy the list and free the associated memory by
  496.      calling the following procedure:
  497.  
  498.           SLLDestroy;
  499.  
  500.           Disposes of all the nodes in the active SingleLL and frees all
  501.      the memory used by the list.
  502.  
  503. Using DoubleLLs
  504.  
  505.           The DoubleLL type is used to create a doubly linked list. The
  506.      Gold routines to manage double linked lists are similar to the ones
  507.      for managing single linked lists, except that the procedure and
  508.      function names begin with DLL instead of SLL.
  509.  
  510.           Listed below are the DoubleLL procedures and functions which
  511.      behave in the same manner as their corresponding SLL functions
  512.      discussed in the previous section:
  513.  
  514. Initializing a DoubleLL variable
  515.  
  516.      InitDLL(var TheList:DoubleLL);
  517.      InitDLLStr(var TheList:DoubleLL);
  518.  
  519. Giving a DoubleLL Focus
  520.  
  521.      DLLSetActiveList(var D:DoubleLL);
  522.      DLLActivatePrevList;
  523.  
  524. Node Pointer Management
  525.  
  526.      DLLNodePtr(NodeNumber:longint): DoubleNodePtr;
  527.  
  528. Adding and Changing List Data
  529.  
  530.      DLLAdd(var TheData;Size:longint): integer;
  531.      DLLChange(Node:DoubleNodePtr;var TheData;Size:longint): integer;
  532.      DLLInsertBefore(Node:DoubleNodePtr;var TheData;Size:longint): int
  533.      DLLDelNode(Node:DoubleNodePtr);
  534.      DLLGetNodeData(Node:DoubleNodePtr;Var TheData);
  535.      DLLGetNodeDataSize(Node:DoubleNodePtr):longint;
  536.  
  537. Storing Strings in a List
  538.  
  539.      DLLAddStr(Str:string):integer;
  540.      DLLGetNodeStr(Node:DoubleNodePtr;Start,Finish: longint): string;
  541.      DLLGetStr(Num:longint): string;
  542.  
  543. Populating a List from a File
  544.  
  545.      DLLLoadFromFile(Filename:string):integer;
  546.      DLLSaveToFile(Filename:string):integer;
  547.  
  548. Settings and Getting Bits
  549.  
  550.      DLLSetBit(Node:DoubleNodePtr; BitPos:byte; On:boolean);
  551.      DLLGetBit(Node:DoubleNodePtr; BitPos:byte): boolean;
  552.      Destroying the List
  553.      DLLDestroy;
  554.  
  555. Defining a Custom String Function
  556.  
  557.           A DoubleLL can store any form of binary data. By default, Gold
  558.      will convert this data to string format whenever the functions
  559.      DLLGetStr or DLLGetNodeStr are called. When the data is binary, all
  560.      Gold can do is assume the data represents characters, and move the
  561.      data into a string format.
  562.  
  563.           Since you know the format of the data (such as a name and
  564.      address record), and you can probably return a much more elegant
  565.      string than this default function. Gold allows you to write your
  566.      own "binary to string" function which will be used in place of
  567.      Gold's default function.
  568.  
  569.           All you have to do is create a function following some
  570.      specific rules and then call the procedure DLLAssignGetStrFunc to
  571.      instruct Gold to call your function every time a binary to string
  572.      conversion is required.
  573.  
  574.           For a procedure to be eligible as a get string function it
  575.      must adhere to the following rules:
  576.  
  577.           The procedure must be declared as a far procedure at the root
  578.           level. Refer to the section Understanding Hooks in chapter 3
  579.           for further information.
  580.  
  581.           The procedure must be declared with three passed parameters.
  582.           The first parameter must be of type DoubleNodePtr, the second
  583.           and third parameters of type longint -- these numbers
  584.           represent the starting and ending characters to be returned.
  585.  
  586.      The following procedure declaration follows these rules:
  587.  
  588.           {$F+}
  589.           function AddrStr(Node:DoubleNodePtr;Start,Finish: longint):
  590.           string;
  591.           {}
  592.           begin
  593.              AddStr := ....
  594.           end; { AddrStr}
  595.           {$F-}
  596.  
  597.           The following procedure is then called to instruct Gold to
  598.      call your procedure to test whether two nodes are in the wrong
  599.      order:
  600.  
  601.      DLLAssignStrFunc(Func:DLLGetStrFunc);
  602.  
  603.           Instructs Gold to call the specified function when getting
  604.      node data in string format.
  605.  
  606. Additional Node Management Functions
  607.  
  608.           Behind the scenes, a double linked list has a pointer called
  609.      the ActiveNodePtr. This pointer is similar to a file cursor, in
  610.      that it points to a node (or line, if you will) in the list.
  611.      Whenever you need to access a node in the list, the ActiveNodePtr
  612.      is set to point to that node. This can speed list management
  613.      operations significantly because there is a high probability that
  614.      the next node to be accessed will be adjacent to the active node
  615.      pointer -- rather than traversing from one end of the list to get
  616.      to the new node, Gold simply shifts one node from the
  617.      ActiveNodePtr.
  618.  
  619.           In most applications you won't need to worry about the
  620.      ActiveNodePtr. However, in addition to the standard node pointer
  621.      function DLLNodePtr, you can use the following procedures to
  622.      manipulate the ActiveNodePtr:
  623.  
  624.           DLLAdvance(Amount:longint);
  625.  
  626.      Moves the ActiveNodePtr Amount nodes down the list.
  627.  
  628.           DLLRetreat(Amount:longint);
  629.  
  630.      Moves the ActiveNodePtr Amount nodes up the list.
  631.  
  632.           DLLJump(NodeNumber:longint);
  633.  
  634.      Moves the ActiveNodePtr to the specified node number.
  635.  
  636.           DLLShiftActiveNode(NewNode: DoubleNodePtr; NodeNumber:
  637.           longint);
  638.  
  639.           Sets the ActiveNodePtr and the ActiveNodeNumber to the
  640.      specified values.
  641.  
  642.           DLLSwapNodes(Node1,Node2:DoubleNodePtr);
  643.  
  644.      Swaps the data stored at the two nodes.
  645.  
  646. Additional Bit Management Functions
  647.  
  648.           In addition to the standard DLLSetBit and DLLGetBit functions,
  649.      there are the following bit management functions:
  650.  
  651.           DLLGetTagState(Num:longint):boolean;
  652.  
  653.           Returns the value of the tag bit (which defaults to bit 0).
  654.      This bit is set by the user in a list window when altering the tag
  655.      state of the item.
  656.  
  657.           DLLDelAllStatus(BitPos:byte;On:boolean);
  658.  
  659.           Deletes all nodes in the list which have the specified bit set
  660.      to the specified state.
  661.  
  662.  
  663.           The demo file DEMLNK2.PAS illustrates many of the DoubleLL
  664.      functions.
  665.  
  666. List Sorting
  667.  
  668.           One fundamental advantage of DoubleLL lists is that the nodes
  669.      can be sorted. Gold has a default sort function which tests the
  670.      data stored at the node like a string, regardless of whether the
  671.      data is actually a string. To sort the list, simply call the
  672.      following procedure:
  673.  
  674.           DLLSort(SortID:shortint; Ascending:boolean);
  675.  
  676.           Sorts the list (i.e. reorders all the nodes) in ascending or
  677.      descending order.  The SortID is only relevant to custom sort
  678.      procedures, and should be specified as zero for the default sort.
  679.  
  680.           If you are using the DoubleLL to store non-string data, you
  681.      might want to incorporate your own sorting routines. For example,
  682.      you might store names and address as records, and want to sort on
  683.      the zip field. If you tend to panic at the thought of having to
  684.      write a sort algorithm, fear not! At the heart of every sort
  685.      algorithm is a simple test to compare whether two nodes are in the
  686.      correct order. For example, is "Bean" before "Ainsbury"? If the
  687.      response is FALSE, the sort function will swap the two nodes, and
  688.      process with the next comparison.
  689.  
  690.           Gold allows you to use a custom function (or sort hook) to
  691.      perform your own wrong order function. Whenever Gold needs to
  692.      decide whether two nodes are in the wrong order, your custom
  693.      function will be called. All you have to do is create a function
  694.      following some specific rules and then call the  procedure
  695.      DLLAssignWrongOrderFunc to instruct Gold to call your function
  696.      every time a node comparison is required.
  697.  
  698.           For a procedure to be eligible as a wrong order function it
  699.      must adhere to the following rules:
  700.  
  701.           The procedure must be declared as a far procedure at the root
  702.           level. Refer to the section Understanding Hooks in chapter 3
  703.           for further information.
  704.  
  705.           The procedure must be declared with four passed parameters.
  706.           The first parameter must be of type shortint, the second and
  707.           third parameters of type DoubleNodePtr variable, and the
  708.           fourth parameter of type boolean.
  709.  
  710.      The following procedure declaration follows these rules:
  711.  
  712.           {$F+}
  713.           function SortHook(SortID:shortint;
  714.           Node1,Node2:DoubleNodePtr; Asc:boolean): boolean;
  715.           {}
  716.           begin
  717.              ...
  718.           end; { SortHook}
  719.           {$F-}
  720.  
  721.           The following procedure is then called to instruct Gold to
  722.      call your procedure to test whether two nodes are in the wrong
  723.      order:
  724.  
  725.           DLLAssignWrongOrderFunc(Func:DLLWrongOrderFunc);
  726.  
  727.           Instructs Gold to call the specified function when sorting a
  728.      double linked list.
  729.  
  730.           The first parameter passed to the wrong order function is the
  731.      parameter which you used when calling the DLLSort function, and it
  732.      is designed to allow you to sort a list in more than one way. The
  733.      example below uses the SortID to decide which field of a record to
  734.      sort on. The second and third parameters are pointers to the two
  735.      nodes which are being sorted. The final parameter is a boolean
  736.      indicating which order to sort the list: ascending or descending.
  737.  
  738.           The best way to explain the sorting concepts is by example.
  739.      Let's assume the active double linked list has been used to store
  740.      records of the following type:
  741.  
  742.         Location: record
  743.            Name: string[20];
  744.            Address: string[50];
  745.            Zip: longint;
  746.         end; {Location}
  747.  
  748.  
  749.           The following function could be used to allow sorting on any
  750.      one of the three fields based on the SortID.
  751.  
  752.           {$F+}
  753.           function RecordSort(SortID:shortint;
  754.           Node1,Node2:DoubleNodePtr; Asc:boolean): boolean;
  755.           {}
  756.           var P1,P2:pointer;
  757.           begin
  758.              P1 := Node1;
  759.              P2 := Node2;
  760.              case SortID of
  761.                 1: begin
  762.                    if Asc then
  763.                       RecordSort := Location(P1^).Name
  764.                                     > Location(P2^).Name
  765.                    else
  766.                       RecordSort := Location(P2^).Name
  767.                                     > Location(P1^).Name;
  768.                 end;
  769.                 2: begin
  770.                    if Asc then
  771.                       RecordSort := Location(P1^).Address
  772.                                     > Location(P2^).Address
  773.                    else
  774.                       RecordSort := Location(P2^).Address
  775.                                     > Location(P1^).Address;
  776.                 end;
  777.                 3: begin
  778.                    if Asc then
  779.                       RecordSort := Location(P1^).Zip
  780.                                     > Location(P2^).Zip
  781.                    else
  782.                       RecordSort := Location(P2^).Zip
  783.                                     > Location(P1^).Zip;
  784.                 end;
  785.              end;
  786.           end; { RecordSort}
  787.           {$F-}
  788.  
  789.           begin
  790.              ...
  791.              DLLAssignWrongOrderFunc(RecordSort);
  792.              ...
  793.           end.
  794.  
  795.           To sort the example list by name in ascending order you would
  796.      make the following call:
  797.  
  798.           DLLSort(1,true);
  799.  
  800.           To sort the example list by zip in descending order you would
  801.      make the following call:
  802.  
  803.           DLLSort(3,false);
  804.  
  805.